/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.irssproject.app.quartz;

import com.irssproject.app.dao.lectorRSS.iface.SubscriptionsDAO;
import com.irssproject.app.dao.main.iface.UsersDAO;
import com.irssproject.app.dao.recomanacions.iface.RecommendationsDAO;
import com.irssproject.app.entitats.Topic;
import com.irssproject.app.entitats.Item;
import com.irssproject.app.entitats.Reading;
import com.irssproject.app.entitats.ReadingProfile;
import com.irssproject.app.entitats.Subscription;
import com.irssproject.app.entitats.User;
import com.irssproject.app.service.recommendations.common.RecommendationsCommon;
import com.irssproject.app.service.recommendations.impl.RecommendationsServiceImpl;
import com.irssproject.app.utils.Constants;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.carrot2.core.Cluster;
import org.carrot2.core.Document;

/**
 *
 * @author adria
 */
public class ClusteringTask {

    private final int MAX_CLUSTERS_SIZE = 100;
    static final Logger logger = Logger.getLogger(RecommendationsServiceImpl.class.getName());
    private SubscriptionsDAO subscripcioDAO;
    private UsersDAO usuariDAO;
    private RecommendationsDAO recomanacionsDAO;
    private ReadingProfile perfil = new ReadingProfile();

    public void setUsersDAO(UsersDAO usuariDAO) {
        this.usuariDAO = usuariDAO;
    }

    public void setRecommendationsDAO(RecommendationsDAO recomanacionsDAO) {
        this.recomanacionsDAO = recomanacionsDAO;
    }

    public void setSubscriptionsDAO(SubscriptionsDAO subscripcioDAO) {
        this.subscripcioDAO = subscripcioDAO;
    }

    public void clusteringProcess() {

        //clean clusters unused

        int from = 0;
        int nmrRecords = 100;
        Long clsNbr = recomanacionsDAO.getClustersNumber();
        List<ReadingProfile> lst = recomanacionsDAO.getAllReadingProfiles(from, nmrRecords);
        while (from<clsNbr) {
            cleanClusters(lst); 
            from += nmrRecords;
            lst = recomanacionsDAO.getAllReadingProfiles(from, nmrRecords);
        }
        
        //Calculate clusters
        List<User> usrs = usuariDAO.getListUsuaris();

        for (User usr : usrs) {

            for (Subscription subscripcio : usr.getSubscripcions()) {
                refreshSubscription(subscripcio);
            }

            long iniTotal = Calendar.getInstance().getTimeInMillis();

            try {
                perfil = new ReadingProfile();
                perfil.setUser(usr);

                List<Subscription> llistaSubscripcions = subscripcioDAO.getListSubscripcions(usr, null);
                for (Subscription subscripcio : llistaSubscripcions) {

                    perfil.setFeed(subscripcio.getFeed());

                    Date now = Calendar.getInstance().getTime();
                    List<Reading> lectures = subscripcioDAO.getReadingsOnPeriod(subscripcio.getLastUpdateDate(), now, subscripcio.getId());

                    if (lectures.size() > subscripcio.getPublishedItemsRatio()) {

                        logger.log(Level.INFO, "********** ADDING A NEW CLUSTER **********");

                        logger.log(Level.INFO, "Subscription: {0}, number of items to process: {1}", new Object[]{subscripcio.getFeed().getTitol(), lectures.size()});

                        List<Cluster> carrot2ClustersList = RecommendationsCommon.lingoClustering(lectures, MAX_CLUSTERS_SIZE).getClusters();

                        logger.log(Level.INFO, "Total number of clusters: {0} elements", carrot2ClustersList.size());

                        for (Cluster cl : carrot2ClustersList) {

                            for (Document doc : cl.getDocuments()) {
                                Item item = subscripcioDAO.getItemByLink(doc.getContentUrl());
                                perfil.setItem(item);

                                String tipusConcepte = Constants.SIMPLE_CONCEPT;

                                if (!cl.getLabel().equals(Constants.OTHER_TOPICS_LABEL)) {

                                    Topic concepte = recomanacionsDAO.getConcepteByTipusValor(tipusConcepte, cl.getLabel());

                                    if (concepte != null) {
                                        ReadingProfile pfl = recomanacionsDAO.getReadingProfile(usr.getId(), subscripcio.getFeed().getId(), item.getId(), concepte.getId());
                                        if (pfl==null) {
                                            perfil.setTopic(concepte);
                                            perfil.setRelevance(1);
                                            recomanacionsDAO.insertReadingProfile(perfil);
                                        } else {
                                            recomanacionsDAO.incrementarFrequenciaConcepte(pfl);
                                        }

                                    } else {
                                        Long idTopic = recomanacionsDAO.insertTopic(tipusConcepte, cl.getLabel());
                                        perfil.setTopic(new Topic(idTopic, cl.getLabel()));
                                        perfil.setRelevance(1);
                                        recomanacionsDAO.insertReadingProfile(perfil);
                                    }

                                }
                            }
                        }

                        subscripcio.setLastUpdateDate(new Date());

                    } else {
                        logger.log(Level.WARNING, "Num of readings since {0}:  {1}. No clustering needed until threshold {2}", new Object[]{subscripcio.getLastUpdateDate(), lectures.size(), subscripcio.getPublishedItemsRatio()});
                    }

                    subscripcio.setPublishedItemsRatio(lectures.size());
                    subscripcioDAO.updateSubscripcio(subscripcio);
                }

            } catch (Exception e) {
                e.printStackTrace();
                logger.log(Level.SEVERE, "Exception while clustering by subscription: {0}", e.getMessage());
            } finally {
                logger.log(Level.INFO, " **************** END OF CLUSTERING PROCESS FOR USER {0}: {1}ms ****************", new Object[]{usr.getLogin(), Calendar.getInstance().getTimeInMillis() - iniTotal});
            }
        }
    }

    private void cleanClusters(List<ReadingProfile> pfls) {
//
//        Calendar cal10DaysAgo = Calendar.getInstance();
//        cal10DaysAgo.add(Calendar.DATE, -10);
//
//        Calendar cal20DaysAgo = Calendar.getInstance();
//        cal20DaysAgo.add(Calendar.DATE, -20);
//
//        for (ReadingProfile pfl : pfls) {
//
//            if (cal20DaysAgo.getTime().compareTo(pfl.getAddedDate()) > 0 && pfl.getRelevance() < 4) {
//                logger.log(Level.WARNING, "deleting topic {0}", pfl.getTopic().getValor());
//                //recomanacionsDAO.deleteCluster(pfl);
//            }
//            if (cal10DaysAgo.getTime().compareTo(pfl.getAddedDate()) > 0 && pfl.getRelevance() < 2) {
//                logger.log(Level.WARNING, "deleting topic {0}", pfl.getTopic().getValor());
//                //recomanacionsDAO.deleteCluster(pfl);
//            }
//
//        }
    }

    private void refreshSubscription(Subscription subs) {
        Item item;
        List<Item> listNousItems = new ArrayList<Item>();

        java.sql.Timestamp timestampDarrerItemPublicat = subscripcioDAO.getDarreraDataPublicacio(subs);
        Date dataDarrerItemPublicat;
        if (timestampDarrerItemPublicat != null) {
            dataDarrerItemPublicat = new Date(timestampDarrerItemPublicat.getTime() + timestampDarrerItemPublicat.getNanos() / 1000000);
        } else {
            dataDarrerItemPublicat = new Date(0L);
        }

        Date dataActualItem;
        try {
            URL feedUrl = new URL(subs.getFeed().getLink());
            final SyndFeedInput input = new SyndFeedInput();
            final SyndFeed feedRSS = input.build(new XmlReader(feedUrl));

            Iterator entries = feedRSS.getEntries().iterator();

            while (entries.hasNext()) {
                item = new Item();

                SyndEntry en = (SyndEntry) entries.next();

                //Si la data de publicacio de la entrada es mes nova que la data de l'ultima lectura
                //l'afegim a la llista de no llegits
                dataActualItem = en.getPublishedDate();

                //La data es opcional, per la qual cosa, si no te data de publicacio mirem si el item existeix per link,
                if (dataActualItem == null) {

                    Item aux = subscripcioDAO.getItemByLink(en.getLink());

                    if (aux == null) {

                        item.setTitle(en.getTitle());
                        item.setLink(en.getLink());
                        item.setPublishedDate(new Date());
                        item.setFeed(subs.getFeed());

                        if (en.getContents().size() > 0) {
                            item.setContent(((SyndContent) en.getContents().get(0)).getValue());
                        } else if (en.getDescription() != null) {
                            item.setContent(en.getDescription().getValue());
                        }
                        logger.log(Level.INFO, "Adding new item from feed {0}  Item name: {1}. No published data (default today): {2}", new Object[]{subs.getFeed().getTitol(), item.getTitle(), item.getPublishedDate()});
                        listNousItems.add(item);

                    }
                } else {
                    if (dataDarrerItemPublicat.getTime() / 1000 < dataActualItem.getTime() / 1000) {
                        item.setTitle(en.getTitle());
                        item.setLink(en.getLink());
                        item.setPublishedDate(en.getPublishedDate());
                        item.setFeed(subs.getFeed());

                        if (en.getContents().size() > 0) {
                            item.setContent(((SyndContent) en.getContents().get(0)).getValue());
                        } else if (en.getDescription() != null) {
                            item.setContent(en.getDescription().getValue());
                        }
                        logger.log(Level.INFO, "Adding new item from feed {0}  Item name: {1}. Published data: {2}", new Object[]{subs.getFeed().getTitol(), item.getTitle(), item.getPublishedDate()});
                        listNousItems.add(item);
                    }
                }
            }

            if (listNousItems.size() > 0) {

                Iterator it = listNousItems.iterator();
                Reading reading;
                Item itemExisting;
                while (it.hasNext()) {
                    reading = new Reading();
                    item = (Item) it.next();
                    itemExisting = subscripcioDAO.getItemByLink(item.getLink());

                    if (itemExisting != null) {
                        reading.setItem(itemExisting);
                    } else {
                        item.setFeed(subs.getFeed());
                        Long id = subscripcioDAO.insertItem(item);
                        item.setId(id);
                        reading.setItem(item);
                    }
                    reading.setSubscription(subs);
                    reading.setRead(false);
                    reading.setReadingTime(0);
                    reading.setRelevance(0);
                    reading.setLiked(false);
                    reading.setAddedDate(item.getPublishedDate());
                    subscripcioDAO.insertReading(reading);
                }
                subscripcioDAO.refreshSubscripcio(subs);
            }
        } catch (FeedException fe) {
            logger.log(Level.SEVERE, "Some problem with feed {0}: {1}", new Object[]{subs.getFeed().getTitol(), fe.getLocalizedMessage()});
        } catch (MalformedURLException mue) {
            logger.log(Level.SEVERE, "Malformed URL {0}: {1}", new Object[]{subs.getFeed().getTitol(), mue.getLocalizedMessage()});
        } catch (IOException fe) {
            logger.log(Level.SEVERE, "Problem with IO {0}: {1}", new Object[]{subs.getFeed().getTitol(), fe.getLocalizedMessage()});
        } catch (Exception e) {
            e.printStackTrace();
            logger.log(Level.SEVERE, "Error adding feed", e.getMessage());
        }

    }
}
